package cn.zxone.feishu.demo.common.handler;

import cn.zxone.feishu.demo.common.enums.ResultCode;
import cn.zxone.feishu.demo.common.exception.BaseException;
import cn.zxone.feishu.demo.common.exception.BusinessException;
import cn.zxone.feishu.demo.common.exception.CustomException;
import cn.zxone.feishu.demo.common.exception.ParamException;
import cn.zxone.feishu.demo.common.pojo.IResultCode;
import cn.zxone.feishu.demo.common.pojo.Result;
import jakarta.servlet.http.HttpServletRequest;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.resource.NoResourceFoundException;

/**
 * <p>
 * 全局异常统一处理
 * </p>
 *
 * @date 2023-07-20 18:15:02
 */
@ResponseBody
@ControllerAdvice
public class GlobalExceptionHandler {

    private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);


    @ExceptionHandler(value = ParamException.class)
    public Result<?> paramExceptionHandler(ParamException exception) {
        log.warn("参数异常：", exception);

        return this.restResult(exception);
    }

    @ExceptionHandler(value = CustomException.class)
    public Result<?> customExceptionHandler(CustomException exception) {
        log.warn("自定义异常：", exception);

        return this.restResult(exception);
    }

    @ExceptionHandler(value = BusinessException.class)
    public Result<?> businessExceptionHandler(BusinessException exception) {
        log.error("业务处理异常：", exception);

        return this.restResult(exception);
    }

    @ExceptionHandler(MissingServletRequestParameterException.class)
    public Result<?> handleMissingServletRequestParameterException(MissingServletRequestParameterException exception) {
        log.warn("缺少Servlet请求参数异常:", exception);

        return Result.failed(ResultCode.PARAM_ERROR, String.format("请求参数[%s]必填", exception.getParameterName()));
    }

    @ExceptionHandler(MethodArgumentTypeMismatchException.class)
    public Result<?> handleMethodArgumentTypeMismatchException(MethodArgumentTypeMismatchException exception) {
        log.warn("方法参数类型不匹配异常:", exception);

        return Result.failed(ResultCode.PARAM_ERROR, String.format("请求参数[%s]类型错误", exception.getName()));
    }

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result<?> handleMethodArgumentNotValidException(MethodArgumentNotValidException exception) {
        log.warn("方法参数无效异常:", exception);

        FieldError fieldError = exception.getBindingResult().getFieldError();

        String field = null;
        String msg = null;
        if (null != fieldError) {
            msg = fieldError.getDefaultMessage();
            field = fieldError.getField();
        }

        if (StringUtils.isBlank(msg)) {
            msg = "参数错误";
        }

        if (StringUtils.isNotBlank(field)) {
            field = "[" + field + "]";
        } else {
            field = "";
        }

        return Result.failed(ResultCode.PARAM_ERROR, String.format(msg, field));
    }

    @ExceptionHandler(BindException.class)
    public Result<?> handleBindException(BindException exception) {
        log.warn("参数绑定异常:", exception);
        FieldError fieldError = exception.getBindingResult().getFieldError();

        String msg = "参数错误";
        if (null != fieldError) {
            msg = fieldError.getDefaultMessage();
        }

        return Result.failed(ResultCode.PARAM_ERROR, msg);
    }

    @ResponseStatus(HttpStatus.BAD_REQUEST)
    @ExceptionHandler(HttpMessageNotReadableException.class)
    public ResponseEntity<Result<?>> handleValidationException(HttpMessageNotReadableException exception) {
        log.warn("Http输入参数不正确:", exception);

        return new ResponseEntity<>(Result.failed(ResultCode.BAD_REQUEST, "请求不正确。请检查输入参数是否正确"), HttpStatus.OK);
    }

    @ResponseStatus(HttpStatus.METHOD_NOT_ALLOWED)
    @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
    public ResponseEntity<Result<?>> handleValidationException(HttpRequestMethodNotSupportedException exception) {
        log.warn("HTTP请求方法不正确:", exception);

        return new ResponseEntity<>(Result.failed(ResultCode.METHOD_NOT_ALLOWED, "请求方法[" + exception.getMethod() + "]不正确"), HttpStatus.OK);
    }

    @ExceptionHandler(NoResourceFoundException.class)
    public ResponseEntity<Result<?>> noResourceFoundExceptionHandler(NoResourceFoundException exception, HttpServletRequest request) {
        log.warn("No resource found exception, request uri: {}, msg: {}", request.getRequestURI(), exception.getMessage(), exception);

        String msg = exception.getResourcePath();

        msg = StringUtils.isBlank(msg) ? "" : ", " + msg;

        return new ResponseEntity<>(this.restResult(new BaseException(ResultCode.NOT_FOUND, exception, "未找到请求资源" + msg)), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(NoHandlerFoundException.class)
    public ResponseEntity<Result<?>> noHandlerFoundExceptionHandler(NoHandlerFoundException exception, HttpServletRequest request) {
        log.warn("No handler found exception, request uri: {}, msg: {}", request.getRequestURI(), exception.getMessage(), exception);

        String msg = exception.getRequestURL();

        msg = StringUtils.isBlank(msg) ? "" : ", " + msg;

        return new ResponseEntity<>(this.restResult(new BaseException(ResultCode.NOT_FOUND, exception, "未找到请求资源" + msg)), HttpStatus.NOT_FOUND);
    }

    @ExceptionHandler(value = Exception.class)
    public ResponseEntity<Result<?>> exceptionHandler(Exception exception) {
        log.error("系统异常：", exception);

        String msg = exception.getMessage();

        msg = StringUtils.isBlank(msg) ? "" : "：" + msg;

        return new ResponseEntity<>(Result.failed(ResultCode.ERROR, "系统错误" + msg), HttpStatus.OK);
    }


    public Result<?> restResult(BaseException baseException) {

        IResultCode resultCode = baseException.getResultCode();
        String message = baseException.getMessage();
        Object data = baseException.getData();

        if (null == resultCode) {
            resultCode = ResultCode.FAILED;
        }

        if (StringUtils.isBlank(message)) {
            message = resultCode.getMessage();
        }

        return Result.failed(resultCode.getCode(), message, data);
    }

}
